package com.xyy.saas.payment.util.mq;

import com.alibaba.fastjson.JSONObject;
import com.xyy.saas.payment.adpater.RouteManager;
import com.xyy.saas.payment.adpater.jd.common.JDConstants;
import com.xyy.saas.payment.callback.core.result.CallbackRunner;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.common.RocketMqGroup;
import com.xyy.saas.payment.common.RocketMqTag;
import com.xyy.saas.payment.common.RocketMqTopic;
import com.xyy.saas.payment.common.constants.MqConstant;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.cores.enums.BusinessIdTypeEnum;
import com.xyy.saas.payment.dao.model.PayResultOrder;
import com.xyy.saas.payment.dao.model.PrePayOrder;
import com.xyy.saas.payment.dao.model.RefundOrder;
import com.xyy.saas.payment.dao.model.ThirdUserInfo;
import com.xyy.saas.payment.merchant.core.dto.CommonRechargePayDto;
import com.xyy.saas.payment.merchant.core.dto.RechargeHandleDto;
import com.xyy.saas.payment.merchant.core.dto.RechargePayDto;
import com.xyy.saas.payment.payment.core.dto.RefundDto;
import com.xyy.saas.payment.payment.core.service.PrePayOrderService;
import com.xyy.saas.payment.payment.core.service.ProfitSharingService;
import com.xyy.saas.payment.payment.core.service.RefundOrderService;
import com.xyy.saas.payment.service.PayResultOrderService;
import com.xyy.saas.payment.service.ThirdUserInfoService;
import com.xyy.saas.payment.trace.AlarmService;
import com.xyy.saas.payment.trace.DingTalkMessage;
import com.xyy.saas.payment.util.ApolloUtil;
import com.xyy.saas.payment.util.DateUtil;
import com.xyy.saas.payment.util.JSONUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.ConsumeMode;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.apache.rocketmq.spring.core.RocketMQPushConsumerLifecycleListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;

@Slf4j
@Service
@RocketMQMessageListener(topic = RocketMqTopic.TOPIC_RECHARGE_HANDLE, consumerGroup = RocketMqGroup.TOPIC_RECHARGE_HANDLE_GROUP, consumeMode = ConsumeMode.CONCURRENTLY)
public class CallbackRechargeHandleConsumer implements RocketMQListener<MessageExt>, RocketMQPushConsumerLifecycleListener {
    @Resource
    private ThirdUserInfoService thirdUserInfoService;
    @Resource
    private PayResultOrderService payResultOrderService;
    @Resource
    private PrePayOrderService prePayOrderService;
    @Resource
    private RouteManager routeManager;
    @Resource
    private RefundOrderService refundOrderService;
    @Autowired
    private AlarmService alarmService;
    @Autowired
    private CallbackRunner callbackRunner;

    @Value("#{'${excludeRechargeUserId:}'.split(',')}")
    private List<String> excludeRechargeUserId;
    @Value("#{'${excludeRechargePayNos:}'.split(',')}")
    private List<String> excludeRechargePayNos;
    @Value("#{'${excludeRechargeRefundNos:}'.split(',')}")
    private List<String> excludeRechargeRefundNos;
    @Autowired
    private ProfitSharingService profitSharingService;

    @Override
    public void onMessage(MessageExt message) {
        String body = new String(message.getBody());
        String tags = StringUtils.isBlank(message.getTags()) ? RocketMqTag.DEFAULT_TAG : message.getTags();
        log.info("CallbackRechargeHandleConsumer#onMessage body:{}, tags:{}", body, tags);
        try {
            RechargeHandleDto param = JSONUtils.parse2Object(body, RechargeHandleDto.class);

            if (param == null) {
                return;
            }

            // 参数校验
            param.checkParam();

            switch (tags) {
                case RocketMqTag.TOPIC_RECHARGE_HANDLE_XYD_CREDIT_PAY_SETTLE_TAG:
                    // 小雨点金融支付结算
                    profitSharingService.xydCreditPaySettle(param.getOrderNo());
                    break;
                case RocketMqTag.TOPIC_RECHARGE_HANDLE_XYD_CREDIT_REFUND_SETTLE_TAG:
                    // 小雨点金融退款结算
                    profitSharingService.xydCreditRefundSettle(param.getOrderNo());
                    break;
                case RocketMqTag.DEFAULT_TAG:
                    // 处理充值
                    doHandleWithDefaultRecharge(param);
                    break;
                default:
                    log.error("CallbackRechargeHandleConsumer unknown tags:{}-{}", tags, param.getOrderNo());
                    break;
            }

        } catch (Exception e) {
            log.error("CallbackRechargeHandleConsumer#onMessage consumer error param:{}", body, e);
            handleErrorRetry(tags, body, e);
        }
    }

    private void doHandleWithDefaultRecharge(RechargeHandleDto param) throws PaymentException {
        // 仅处理直连、京东银行卡支付
        if (!param.getChannel().contains(Constants.CHANNEL_DIRECT_NAME) && !Constants.thirdChannelSubmitPingan(param.getChannel())) {
            log.info("渠道不为直连或者三方渠道报送平安，不进行后续充值处理{}", JSONUtils.toJSON(param));
            return;
        }

        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(getPayeeAccountId(param.getAccountId()));

        if (thirdUserInfo == null) {
            log.info("未查询到平安开户信息，不进行后续处理{}", JSONUtils.toJSON(param));
            return;
        }

        // 非平安不处理
        if (!Constants.CHANNEL_PINGAN_NAME.equals(thirdUserInfo.getThirdType())) {
            log.info("CallbackRechargeHandleConsumer#onMessage ignore(Not Ping An channel)，param={}", JSONUtils.toJSON(param));
            return;
        }

        if (RechargeHandleDto.PAY.equals(param.getRechargeType())) {
            handlePay(param, thirdUserInfo);
        } else if (RechargeHandleDto.REFUND.equals(param.getRechargeType())) {
            handleRefund(param, thirdUserInfo);
        }
    }

    private void handleErrorRetry(String tags, String body, Exception e) {
        JSONObject param = JSONObject.parseObject(body);
        int times = (int) param.getOrDefault("times", 0);
        if (times < 3) {
            param.put("times", ++times);
            //callbackRunner.syncSendDelayMsg(RocketMqTopic.TOPIC_RECHARGE_HANDLE, param, MqConstant.DELAY_LEVELS[times]);
            callbackRunner.syncSendDelayMsgWithTag(RocketMqTopic.TOPIC_RECHARGE_HANDLE, tags, param, MqConstant.DELAY_LEVELS[times]);
        } else {
            String alarmType = getAlarmType(tags);
            DingTalkMessage msg = DingTalkMessage.builder().type(alarmType).method("").msg("重试次数:" + times + "," + e.getMessage()).param(body).build();
            alarmService.alarm(msg);
        }
    }

    private String getAlarmType(String tags) {
        switch (tags) {
            case RocketMqTag.TOPIC_RECHARGE_HANDLE_XYD_CREDIT_PAY_SETTLE_TAG:
                return "小雨点金融支付结算";
            case RocketMqTag.TOPIC_RECHARGE_HANDLE_XYD_CREDIT_REFUND_SETTLE_TAG:
                return "小雨点金融退款结算";
            case RocketMqTag.DEFAULT_TAG:
                return "直连渠道上报平安充值";
            default:
                return "";
        }
    }

    @Override
    public void prepareStart(DefaultMQPushConsumer consumer) {
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
    }

    /**
     * 处理充值
     */
    public void handlePay(RechargeHandleDto param) throws PaymentException {
        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(getPayeeAccountId(param.getAccountId()));
        if(thirdUserInfo == null) {
            log.info("CallbackRechargeHandleConsumer#handlePay thirdUserInfo is null , {}", JSONUtils.toJSON(param));
            return;
        }
        handlePay(param, thirdUserInfo);
    }

    /**
     * 处理充值
     */
    private void handlePay(RechargeHandleDto param, ThirdUserInfo thirdUserInfo) throws PaymentException {
        log.info("CallbackRechargeHandleConsumer#handlePay param={}", JSONUtils.toJSON(param));
        PayResultOrder resultOrder = payResultOrderService.selectByPayNoAndChannel(param.getOrderNo(), param.getChannel());

        if(resultOrder == null) {
            log.warn("CallbackRechargeHandleConsumer#handlePay ignore(PayResultOrder is NULL)，param={}", JSONUtils.toJSON(param));
            return;
        }

        if (CollectionUtils.isNotEmpty(excludeRechargeUserId) && excludeRechargeUserId.contains(resultOrder.getReceiveId())) {
            log.error("CallbackRechargeHandleConsumer#handlePay excludeRechargeUserId, payNo:{}, userId:{}", param.getOrderNo(), resultOrder.getReceiveId());
            return;
        }

        if (CollectionUtils.isNotEmpty(excludeRechargePayNos) && excludeRechargePayNos.contains(resultOrder.getPayNo())) {
            log.error("CallbackRechargeHandleConsumer#handlePay excludeRechargePayNos, payNo:{}", resultOrder.getPayNo());
            return;
        }

        // 仅处理 直连微信、直连支付宝、京东银行卡支付
        if(!"1629113578".equals(resultOrder.getMerchantId()) && !"2021003155680358".equals(resultOrder.getMerchantId()) && !Constants.thirdChannelSubmitPingan(param.getChannel())) {
            log.warn("CallbackRechargeHandleConsumer#handlePay ignore(非北京药帮忙且非京东银行卡支付，不需要充值)，param={}", JSONUtils.toJSON(param));
            return;
        }

        if(PayResultOrder.ROYALTY__STATUS_HAVE.equals(resultOrder.getRoyalty())) {
            log.warn("CallbackRechargeHandleConsumer#handlePay already royalty，param={}, resultOrder={}",
                    JSONUtils.toJSON(param), JSONUtils.toJSON(resultOrder));
            return;
        }

        if(PayResultOrder.ROYALTY__STATUS_REFUNDED.equals(resultOrder.getRoyalty())) {
            log.warn("CallbackRechargeHandleConsumer#handlePay already refund royalty, param={}, resultOrder={}",
                    JSONUtils.toJSON(param), JSONUtils.toJSON(resultOrder));
            return;
        }

        // 若为京东银行卡支付，则转换商户号
        if (Constants.thirdChannelSubmitPingan(param.getChannel())) {
            resultOrder.setMerchantId(JDConstants.merchantSubMerchant("handlePay", resultOrder.getMerchantId()));
            if (StringUtils.isBlank(resultOrder.getMerchantId())) {
                log.warn("CallbackRechargeHandleConsumer#handlePay 未获取到分账商户号，不进行充值处理，param={}, resultOrder={}", JSONUtils.toJSON(param), JSONUtils.toJSON(resultOrder));
                return;
            }
        }

        PrePayOrder prePayOrder = prePayOrderService.getPrePayOrderByPayNo(resultOrder.getPayNo());
        Object withdrawCardTypeObj = JSONUtils.getValueFromJson(prePayOrder.getExtra(), "settleWithdrawType");
        String withdrawCardType = withdrawCardTypeObj == null ? "": (String) withdrawCardTypeObj;
        //提现至个人卡，不上报平安
        if (Constants.PERSONAL_WITHDRAW_CARD.equals(withdrawCardType)) {
            return;
        }
        // 1.大商户和自营非冻结充值
        if(Objects.equals(ApolloUtil.getProperty(Constants.EC_POP_BIG_SHOP_PINGAN_ACCOUNT_ID), thirdUserInfo.getUserId())
                || prePayOrder.getBusinessOrderType().contains(BusinessIdTypeEnum.EC_SELF_SUPPORT.getType()))
        {
            CommonRechargePayDto commonRechargePayDto = CommonRechargePayDto.builder().
                    businessIdType(resultOrder.getBusinessOrderType()).
                    accountId(thirdUserInfo.getUserId()).
                    channel(RechargePayDto.convertChannel(resultOrder.getPayChannel())).
                    channelMerchantNo(resultOrder.getMerchantId()).
                    channelTradeNo(resultOrder.getChannelTransactionNo()).
                    tradeAmount(resultOrder.getAmount()).
                    build();
            commonRechargePayDto.buildDetail(resultOrder.getPayNo(), thirdUserInfo.getThirdUserId(), resultOrder.getAmount(), prePayOrder.getBusinessOrderNo());
            routeManager.commonRechargePay(commonRechargePayDto);
        }
        // 1.冻结充值
        else {
            RechargePayDto rechargePayDto = RechargePayDto.builder().
                    businessIdType(resultOrder.getBusinessOrderType()).
                    accountId(resultOrder.getReceiveId()).
                    channel(RechargePayDto.convertChannel(resultOrder.getPayChannel())).
                    channelMerchantNo(resultOrder.getMerchantId()).
                    tradeDate(resultOrder.getCtime()).
                    channelTradeNo(resultOrder.getChannelTransactionNo()).
                    tradeAmount(resultOrder.getAmount()).
                    build();
            rechargePayDto.buildDetail(resultOrder.getPayNo(), thirdUserInfo.getThirdUserId(), resultOrder.getAmount(), prePayOrder.getBusinessOrderNo());
            routeManager.rechargePay(rechargePayDto);
        }

        // 2.变更充值状态
        PayResultOrder updatePayResultOrder = new PayResultOrder();
        updatePayResultOrder.setId(resultOrder.getId());
        updatePayResultOrder.setRoyalty(PayResultOrder.ROYALTY__STATUS_HAVE);
        updatePayResultOrder.setUtime(DateUtil.now());
        payResultOrderService.updateByPrimaryKeySelective(updatePayResultOrder);
    }

    /**
     * 处理充值
     */
    public void handleRefund(RechargeHandleDto param) throws PaymentException {
        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(getPayeeAccountId(param.getAccountId()));
        if(thirdUserInfo == null) {
            log.info("CallbackRechargeHandleConsumer#handleRefund thirdUserInfo is null , {}", JSONUtils.toJSON(param));
            return;
        }
        handleRefund(param, thirdUserInfo);
    }

    /**
     * 处理充值撤销
     */
    private void handleRefund(RechargeHandleDto param, ThirdUserInfo thirdUserInfo) throws PaymentException {
        log.info("CallbackRechargeHandleConsumer#handleRefund param={}", JSONUtils.toJSON(param));

        // 1.单状态校验
        RefundOrder refundOrder = refundOrderService.selectByRefundNo(param.getOrderNo());

        if(!RefundOrder.STATUS_FINISH.equals(refundOrder.getStatus())) {
            log.info("CallbackRechargeHandleConsumer#handleRefund，状态不为完成，不进行后续处理。param={}", JSONUtils.toJSON(param));
            return;
        }

        if (CollectionUtils.isNotEmpty(excludeRechargeUserId) && excludeRechargeUserId.contains(refundOrder.getUserId())) {
            log.error("CallbackRechargeHandleConsumer#handleRefund excludeRechargeUserId, refundNo:{}, userId:{}", param.getOrderNo(), refundOrder.getUserId());
            return;
        }

        if (CollectionUtils.isNotEmpty(excludeRechargeRefundNos) && excludeRechargeRefundNos.contains(param.getOrderNo())) {
            log.error("CallbackRechargeHandleConsumer#handlePay excludeRechargeRefundNos, refundNo:{}", param.getOrderNo());
            return;
        }

        PayResultOrder resultOrder = payResultOrderService.selectByPayNoAndChannel(refundOrder.getPayNo(), refundOrder.getChannel());

        // 仅处理 直连微信、直连支付宝、京东银行卡支付
        if(!"1629113578".equals(resultOrder.getMerchantId()) && !"2021003155680358".equals(resultOrder.getMerchantId()) && !Constants.thirdChannelSubmitPingan(refundOrder.getChannel())) {
            log.warn("CallbackRechargeHandleConsumer#handleRefund ignore(非北京药帮忙不需要撤销)，param={}", JSONUtils.toJSON(param));
            return;
        }

        if(PayResultOrder.ROYALTY_STATUS_NONE.equals(resultOrder.getRoyalty())) {
            log.warn("CallbackRechargeHandleConsumer#handleRefund none royalty, param:{}, resultOrder:{}",
                    JSONUtils.toJSON(param), JSONUtils.toJSON(resultOrder));
            return;
        }
        // 若为京东银行卡支付，则转换商户号
        if (Constants.thirdChannelSubmitPingan(param.getChannel())) {
            resultOrder.setMerchantId(JDConstants.merchantSubMerchant("handleRefund", resultOrder.getMerchantId()));
            if (StringUtils.isBlank(resultOrder.getMerchantId())) {
                log.warn("CallbackRechargeHandleConsumer#handlePay 未获取到分账商户号，不进行充值处理，param={}, resultOrder={}", JSONUtils.toJSON(param), JSONUtils.toJSON(resultOrder));
                return;
            }
        }

        PrePayOrder prePayOrder = prePayOrderService.getPrePayOrderByPayNo(resultOrder.getPayNo());
        Object withdrawCardTypeObj = JSONUtils.getValueFromJson(prePayOrder.getExtra(), "settleWithdrawType");
        String withdrawCardType = withdrawCardTypeObj == null ? "": (String) withdrawCardTypeObj;
        //提现至个人卡，不上报平安
        if ( Constants.PERSONAL_WITHDRAW_CARD.equals(withdrawCardType)) {
            return;
        }
        // 2.退款
        RefundDto refundDto = new RefundDto();
        refundDto.setChannel(resultOrder.getPayChannel());
        refundDto.setChannelChargeId(resultOrder.getChannelChargeNo());
        refundDto.setPayNo(resultOrder.getPayNo());
        refundDto.setRefundNo(refundOrder.getRefundNo());
        refundDto.setTotalAmount(refundOrder.getAmount());
        refundDto.setAmount(refundOrder.getAmount());
        refundDto.setBusinessRefundNo(refundOrder.getBusinessRefundNo());
        refundDto.setCallbackUrl(refundOrder.getCallbackAddress());
        refundDto.setBusinessType(resultOrder.getBusinessType());
        refundDto.setBusinessOrderType(resultOrder.getBusinessOrderType());
        refundDto.setUserId(thirdUserInfo.getUserId());
        refundDto.setMerchantId(resultOrder.getMerchantId());
        refundDto.setPayer(resultOrder.getPayer());
        refundDto.setChannelTransactionNo(refundOrder.getChannelRefundNo());
        refundDto.setOrigChannelTransactionNo(resultOrder.getChannelTransactionNo());
        refundDto.setOriTransDate(resultOrder.getCtime());
        // 订单发送时间与当前时间间隔不能超过5分钟
        refundDto.setTransDate(DateUtil.now());
        refundDto.setPayMode(resultOrder.getPayMode());
        refundDto.setReceiverId(thirdUserInfo.getUserId());
        refundDto.setBusinessOrderNo(prePayOrder.getBusinessOrderNo());
        refundDto.setBusinessIdTypeRoute(Boolean.TRUE);
        routeManager.refund(refundDto);

        // 3.变更充值状态
        PayResultOrder updatePayResultOrder = new PayResultOrder();
        updatePayResultOrder.setId(resultOrder.getId());
        updatePayResultOrder.setRoyalty(PayResultOrder.ROYALTY__STATUS_REFUNDED);
        updatePayResultOrder.setUtime(DateUtil.now());
        payResultOrderService.updateByPrimaryKeySelective(updatePayResultOrder);
    }

    /**
     * 适配大商户
     */
    private String getPayeeAccountId(String accountId) {
        if(Boolean.parseBoolean(ApolloUtil.getProperty(Constants.EC_POP_BIG_SHOP_TO_PINGAN_SWITCH, Boolean.FALSE.toString())) &&
                Objects.equals(ApolloUtil.getProperty(Constants.POP_DIRECT_PAY_YBM_TECH_USER_ID), accountId))
        {
            return ApolloUtil.getProperty(Constants.EC_POP_BIG_SHOP_PINGAN_ACCOUNT_ID);
        }
        else {
            return accountId;
        }
    }
}
